package com.heima.article.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.*;
import com.heima.article.service.ApArticleService;
import com.heima.common.constants.article.ArticleConstants;
import com.heima.common.exception.CustException;
import com.heima.common.exception.CustomException;
import com.heima.feigns.article.ArticleFeign;
import com.heima.feigns.behavior.BehaviorFeign;
import com.heima.feigns.user.UserFeign;
import com.heima.model.article.dto.ArticleDto;
import com.heima.model.article.dto.ArticleHomeDto;
import com.heima.model.article.dto.ArticleInfoDto;
import com.heima.model.article.mess.ArticleVisitStreamMess;
import com.heima.model.article.pojo.*;
import com.heima.model.article.vo.HotArticleVo;
import com.heima.model.behavior.dto.ApArticleRelationDto;
import com.heima.model.behavior.pojo.ApBehaviorEntry;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.user.pojo.ApUser;
import com.heima.model.user.pojo.ApUserFollow;
import com.heima.utils.threadlocal.AppThreadLocalUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

//public class ApArticleServiceImpl extends   implements ApArticleService {
@Service
@Slf4j
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper,ApArticle> implements ApArticleService {

    @Autowired
    private AuthorMapper authorMapper;

    @Autowired
    private ApArticleConfigMapper apArticleConfigMapper;

    @Autowired
    private ApArticleContentMapper apArticleContentMapper;
    @Autowired
    private ApArticleMapper apArticleMapper;
    @Value("${file.oss.web-site}")
    private String webSite;
    @Autowired
    private ArticleFeign articleFeign;
    @Autowired
    private UserFeign userFeign;
    @Autowired
    private BehaviorFeign behaviorFeign;
    @Autowired
    private ApCollectionMapper apCollectionMapper;
    /**
     * 保存三张表的信息
     * @param articleDto
     * @return
     */
    @Override
    public ResponseResult saveArticle(ArticleDto articleDto) {
        // 1. 检验参数
        // 2. 将dto转为article对象
        ApArticle apArticle = new ApArticle();
        BeanUtils.copyProperties(articleDto,apArticle);
        // 3. 根据作者名称查询作者ID
        ApAuthor author = authorMapper.selectOne(Wrappers.<ApAuthor>lambdaQuery().eq(ApAuthor::getName, articleDto.getAuthorName()));
        if(author == null){
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST,"作者信息不存在");
        }
        apArticle.setAuthorId(Long.valueOf(author.getId()));
        // 4. 判断文章id是否存在
        if(apArticle.getId() == null){
            // 4.1 如果id为空  保存文章
            apArticle.setLikes(0);
            apArticle.setComment(0);
            apArticle.setViews(0);
            apArticle.setCollection(0);
            save(apArticle);
        }else {
            // 4.2 如果id不为空  修改文章
            updateById(apArticle);
            //   将之前关联的文章配置 和 文章内容删除
            apArticleConfigMapper.delete(Wrappers.<ApArticleConfig>lambdaQuery().eq(ApArticleConfig::getArticleId,apArticle.getId()));
            apArticleContentMapper.delete(Wrappers.<ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId,apArticle.getId()));
        }
        // 5.  统一保存  配置信息  保存  文章内容信息
        ApArticleConfig config = new ApArticleConfig();
        config.setArticleId(apArticle.getId());
        config.setIsComment(true); // 可以评论
        config.setIsForward(true); // 可以转发
        config.setIsDown(false);  // 上架
        config.setIsDelete(false); // 未删除
        apArticleConfigMapper.insert(config);
        ApArticleContent content = new ApArticleContent();
        content.setArticleId(apArticle.getId());
        content.setContent(articleDto.getContent());
        apArticleContentMapper.insert(content);
        // 6.  返回文章的id
        return ResponseResult.okResult(apArticle.getId());
    }

    @Override
    public ResponseResult load(Short loadType, ArticleHomeDto dto) {
        //1.检验参数
        if (dto.getSize() ==null || dto.getSize()<=0) {
            dto.setSize(10);
        }
        if (dto.getMaxBehotTime() ==null) {
            dto.setMaxBehotTime(new Date());
        }
        if (dto.getMinBehotTime() ==null) {
            dto.setMinBehotTime(new Date());
        }
        if (dto.getTag() ==null) {
            dto.setTag(ArticleConstants.DEFAULT_TAG);
        }
        //2.执行查询
        List<ApArticle> apArticleList = this.apArticleMapper.loadArticleList(dto, loadType);
        //3.返回结果
        ResponseResult result = ResponseResult.okResult(apArticleList);
        result.setHost(webSite);
        return result;
    }

    @Override
    public ResponseResult loadArticleInfo(ArticleInfoDto dto) {
        // 1. 检查参数
        if(dto.getArticleId()  == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"文章id不能为空");
        }
        // 2. 先查配置信息  上架  删除
        ApArticleConfig config = apArticleConfigMapper.selectOne(Wrappers.<ApArticleConfig>lambdaQuery().eq(ApArticleConfig::getArticleId, dto.getArticleId()));
        if(config == null || config.getIsDelete() || config.getIsDown()){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"查询文章配置信息出错");
        }
        // 3. 查询文章内容
        ApArticleContent content = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId, dto.getArticleId()));
        if(content == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"未查询到该文章信息");
        }
        // 4. 封装返回结果
        Map map = new HashMap<>();
        map.put("content",content);
        map.put("config",config);
        return ResponseResult.okResult(map);
    }

    @Override
    public ResponseResult loadArticleBehavior(ArticleInfoDto dto) {
        boolean isfollow=false,iscollection=false;
        //1. 检查参数
        if (dto.getArticleId() == null ||dto.getAuthorId() ==null) {
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2. 查询登录用户id  远程查询关注信息
        ApUser user = AppThreadLocalUtils.getUser();
        //ApAuthor apAuthor = articleFeign.findByUserId(user.getId());
        //if (user !=null&& apAuthor !=null){
        //    ApUserFollow apUserFollow = userFeign.findByUserIdAndFollowId(user.getId(), apAuthor.getUserId());
        //    if (apUserFollow !=null) {
        //        isfollow =true;
        //    }
        //}
        // 是否关注
        ApAuthor apAuthor = authorMapper.selectById(dto.getAuthorId());
        if(apAuthor!=null){
            ApUserFollow apUserFollow = userFeign.findByUserIdAndFollowId(
                    AppThreadLocalUtils.getUser().getId(), apAuthor.getUserId());
            if(apUserFollow!=null){
                isfollow=true;
            }
        }

        //3. 远程查询行为微服务  查询点赞 和 不喜欢的信息
        ApArticleRelationDto relationDto = new ApArticleRelationDto();
        relationDto.setArticleId(dto.getArticleId()); // 文章id
        if(user==null){
            relationDto.setEntryId(dto.getEquipmentId());
            relationDto.setType(ApBehaviorEntry.Type.EQUIPMENT.getCode()); // 0 用户   1 设备
        }else {
            relationDto.setEntryId(user.getId());
            relationDto.setType(ApBehaviorEntry.Type.USER.getCode()); // 0 用户   1 设备
        }
        // islike  isunlike
        Map result = behaviorFeign.findApArticleRelation(relationDto);
        Object entryId = result.get("entryId");
        //4. 根据文章id 和 行为实体id在当前微服务中 查询是否收藏的信息
        if(entryId!=null) {
            // islike  isunlike
            ApCollection apCollection = this.apCollectionMapper.selectOne(Wrappers.<ApCollection>lambdaQuery()
                    .eq(ApCollection::getArticleId, dto.getArticleId())
                    .eq(ApCollection::getEntryId, entryId));
            if (apCollection != null) {
                iscollection = true;
            }
        }
        //5. 将所有信息封装成一个map集合返回前台
        result.put("isfollow",isfollow);
        result.put("iscollection",iscollection);
        return ResponseResult.okResult(result);

    }

    @Autowired
    RedisTemplate<String,String> redisTemplate;
    /**
     * 重新计算文章分值
     * @param mess
     */
    @Override
    public void updateApArticle(ArticleVisitStreamMess mess) {
        log.info("updateApArticle is begin: {}",mess);
        //1 查询文档
        ApArticle apArticle = getById(mess.getArticleId());
        if (apArticle == null) {
            log.error("apArticle is null id:{}", mess.getArticleId());
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //2 修改文章的行为数据（阅读1、点赞3、评论5、收藏8）
        if (mess.getView() != 0) {
            int view = (int) (apArticle.getViews() == null ? mess.getView() : mess.getView() + apArticle.getViews());
            apArticle.setViews(view);
        }
        if (mess.getLike() != 0) {
            int like = (int) (apArticle.getLikes() == null ? mess.getLike() : mess.getLike() + apArticle.getLikes());
            apArticle.setLikes(like);
        }
        if (mess.getComment() != 0) {
            int comment = (int) (apArticle.getComment() == null ? mess.getComment() : mess.getComment() + apArticle.getComment());
            apArticle.setComment(comment);
        }
        if (mess.getCollect() != 0) {
            int collection = (int) (apArticle.getCollection() == null ? mess.getCollect() : mess.getCollect() + apArticle.getCollection());
            apArticle.setCollection(collection);
        }
        updateById(apArticle);
        //3 计算文章分值
        Integer score = computeScore(apArticle);
        score = score*3;  //当天热点数据 *3
        //4 更新缓存（频道）
        updateArticleCache(apArticle, score, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId());
        //5 更新推荐列表的缓存
        updateArticleCache(apArticle, score,  ArticleConstants.HOT_ARTICLE_FIRST_PAGE+ ArticleConstants.DEFAULT_TAG);
        log.info("updateApArticle is success");
    }
    /**
     * 更新文章缓存
     * @param apArticle  当前文章
     * @param score 分数
     * @param cacheKey
     */
    private void updateArticleCache(ApArticle apArticle, Integer score, String cacheKey) {
        log.info("updateApArticle updateArticleCache apArticle:{},score:{}",apArticle,score);
        boolean flag = false;
        String hotArticleListJson = redisTemplate.opsForValue()
                .get(cacheKey);
        if (StringUtils.isNotBlank(hotArticleListJson)) {
            List<HotArticleVo> hotArticleList = JSONArray.parseArray(hotArticleListJson,HotArticleVo.class);
            //1 如果当前缓存中有当前文章，更新分值
            for (HotArticleVo hotArticleVo : hotArticleList) {
                if (hotArticleVo.getId().equals(apArticle.getId())) {
                    hotArticleVo.setScore(score);
                    flag = true;
                    break;
                }
            }
            //2 缓存中没有当前文章
            if (!flag) {
                HotArticleVo hotArticle = new HotArticleVo();
                BeanUtils.copyProperties(apArticle, hotArticle);
                hotArticle.setScore(score);
                hotArticleList.add(hotArticle);
            }
            //3. 将热点文章集合 按得分降序排序  取前30条缓存至redis中
            hotArticleList = hotArticleList.stream()
                    .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                    .limit(30)
                    .collect(Collectors.toList());
            log.info("updateApArticle updateArticleCache success");
            redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(hotArticleList));
        }
    }
    /**
     * 2.1计算文章分值算法
     * @param apArticle
     * @return
     */
    private Integer computeScore(ApArticle apArticle) {
        int score = 0;
        // 阅读 1
        if (apArticle.getViews() != null) {
            score += apArticle.getViews() * ArticleConstants.HOT_ARTICLE_VIEW_WEIGHT;
        }
        // 点赞 3
        if (apArticle.getLikes() != null) {
            score += apArticle.getLikes() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        // 评论 5
        if (apArticle.getComment() != null) {
            score += apArticle.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        // 收藏 8
        if (apArticle.getCollection() != null) {
            score += apArticle.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }
        return score;
    }

    /**
     * 根据参数加载文章列表  v2
     *
     * @param loadtypeLoadMore
     * @param dto
     * @param firstPage
     * @return
     */
    @Override
    public ResponseResult load2(Short loadtypeLoadMore, ArticleHomeDto dto, boolean firstPage) {
        if(firstPage){
            String jsonStr = (String) redisTemplate.opsForValue().get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + dto.getTag());
            if(StringUtils.isNotBlank(jsonStr)){
                List<HotArticleVo> hotArticleVoList = JSON.parseArray(jsonStr, HotArticleVo.class);
                if(!hotArticleVoList.isEmpty()&& hotArticleVoList.size() > 0){
                    ResponseResult responseResult = ResponseResult.okResult(hotArticleVoList);
                    responseResult.setHost(webSite);
                    return responseResult;
                }
            }
        }
        return load(loadtypeLoadMore,dto);
    }
}